//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "CFontConverter.h"
#include "..\\..\\Core\\CConverter.h"

CFontConverter::CFontConverter():
	_uASCIIoffset(0), _uUnicodeOffset(0)
{
}


CFontConverter::~CFontConverter()
{
}
	

HRESULT CALLBACK CFontConverter::GetSupportedFormats(char *pBuffer, uint uCount)
{
	static const string		s_sFormats = string("font|") << "*.dft";

	if ( pBuffer == nullptr || uCount == 0 )
		return E_INVALIDARG;

	if ( uCount > s_sFormats.Length() )
	{
		memcpy( pBuffer, s_sFormats.cstr(), s_sFormats.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CALLBACK CFontConverter::Create()
{
	_uASCIIoffset	= 0;
	_uUnicodeOffset	= 0;

	_sImage.Clear();
	_aChars.Clear();

	return S_OK;
}


HRESULT CALLBACK CFontConverter::Delete()
{
	_uASCIIoffset	= 0;
	_uUnicodeOffset	= 0;

	_sImage.Clear();
	_aChars.Clear();

	return S_OK;
}


HRESULT CALLBACK CFontConverter::Load(const char *pcFileName)
{
	IFilePtr	p_file = nullptr;

	if ( CORE->LoadFile( pcFileName, p_file, e_file_open_flags::DEF_READ ) != S_OK )
		return E_FAIL;

	const char	*p_ext = String::GetFileExt( pcFileName );
	HRESULT		ret = 0;

	if ( p_ext == nullptr )
		return E_FAIL;
	
	if ( strcmp( p_ext, "dft" ) == 0 )
	{
		ret = _LoadFontDFT( p_file ) ? S_OK : E_FAIL;
	}
	else
	{
		LOG( (string("Unknown file extension: \"") << p_ext << "\".").cstr(), );
		ret = E_FAIL;
	}

	return ret;
}


HRESULT CALLBACK CFontConverter::Save(const char *pcFileName)
{
	IFilePtr	p_file = nullptr;

	if ( CORE->LoadFile( pcFileName, p_file, e_file_open_flags::DEF_WRITE ) != S_OK )
		return E_FAIL;

	const char	a_file_header[] = "FNT "FNTVER" BIN ";

#pragma pack(push, 1)
	struct TFontFileHeader
	{
		uint	uHeaderSize,
				uUserDataSize;
	};

	struct TFontHeader
	{
		uint	uCount;
		uint	uUniOffset;
		uint8	uAscOffset;
	};
#pragma pack(pop)

	TFontFileHeader	s_fheader;
	TFontHeader		s_header;

	s_fheader.uHeaderSize	= sizeof(s_header);
	s_fheader.uUserDataSize	= 0;

	s_header.uCount			= _aChars.Count();
	s_header.uAscOffset		= (uint8)_uASCIIoffset;
	s_header.uUniOffset		= _uUnicodeOffset;

	if ( s_header.uCount == 0 )
		LOG( "0 simbol chars.", );

	p_file->WriteS( a_file_header, 12 );
	p_file->WriteS( &s_fheader, sizeof(s_fheader) );
	p_file->WriteS( &s_header,  sizeof(s_header) );

	p_file->WriteS( _aChars.ptr(), _aChars.Size() );

	HRESULT	ret = 0;

	ret = Loaders::SaveImage2D( p_file, _sImage, e_image::TEXTURE_2D ) ? S_OK : E_FAIL;

	return ret;
}


HRESULT CALLBACK CFontConverter::SetASCOffset(uint uOffset)
{
	if ( uOffset >= UINT8_MAX )
		return E_INVALIDARG;

	_uASCIIoffset = uOffset;
	return S_OK;
}


HRESULT CALLBACK CFontConverter::SetUnicodeOffset(uint uOffset)
{
	if ( uOffset >= UINT16_MAX )
		return E_INVALIDARG;

	_uUnicodeOffset = uOffset;
	return S_OK;
}


HRESULT CALLBACK CFontConverter::SetCharsCount(uint uCount, uint uDefChar)
{
	if ( uCount < _aChars.Count() )
	{
		_aChars.Resize( uCount, false );
		return S_OK;
	}
	else
	if ( uCount > _aChars.Count() )
	{
		if ( uDefChar >= _aChars.Count() ) {
			LOG( (string("can't use char: \'") << uDefChar << "\'.").cstr(), );
			uDefChar = 0;
		}

		for (uint i = (uint)_aChars.Count(); i < uCount; ++i)
			_aChars.PushBack( _aChars[uDefChar] );

		return S_OK;
	}
	else
		return S_FALSE;
}


HRESULT CALLBACK CFontConverter::GetASCOffset(uint &uOffset)
{
	uOffset = _uASCIIoffset;
	return S_OK;
}


HRESULT CALLBACK CFontConverter::GetUnicodeOffset(uint &uOffset)
{
	uOffset = _uUnicodeOffset;
	return S_OK;
}


HRESULT CALLBACK CFontConverter::GetCharsCount(uint &uCount)
{
	uCount = _aChars.Count();
	return S_OK;
}


HRESULT CALLBACK CFontConverter::GetType(E_SUBSYSTEM_TYPE &eType)
{
	eType = SST_FONT_CONVERTER;
	return S_OK;
}


bool CFontConverter::_LoadFontDFT(IFilePtr pFile)
{
	char	ac_file_header[8];

	pFile->ReadS( ac_file_header, 7 );
	ac_file_header[7] = '\0';

	if ( strcmp( ac_file_header, "DFT 1.1" ) != 0 ) {
		LOG( "Incorrect DFT file header.", );
		return false;
	}

#pragma pack(push, 1)
	struct TDFTHeader
	{
		uint8	uBitDepth;
		uint	uTexWidth,
				uTexHeight;
	};
#pragma pack(pop)

	TDFTHeader	s_header;

	pFile->ReadS( &s_header, sizeof(s_header) );

	rectu	a_chars_rect[224];
	uint	u_size = s_header.uBitDepth * s_header.uTexHeight * s_header.uTexWidth;
	GLenum	ifmt	= 0,
			fmt		= 0,
			type	= GL_UNSIGNED_BYTE;
	GLint	swizzle[4];
	
	if ( s_header.uBitDepth == 1 ) {
		GLint	a_temp_sw[4] = { GL_ONE, GL_ONE, GL_ONE, GL_RED };
		memcpy( swizzle, a_temp_sw, sizeof(swizzle) );
		ifmt = GL_RED;
		fmt  = GL_RED;
	}
	else
	if ( s_header.uBitDepth == 4 ) {
		GLint	a_temp_sw[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
		memcpy( swizzle, a_temp_sw, sizeof(swizzle) );
		ifmt = GL_RGBA;
		fmt  = GL_RGBA;
	}
	else
	{
		LOG( "Incorrect texture bit depth.", );
		return false;
	}

	_sImage.vTexDim.x = s_header.uTexWidth;
	_sImage.vTexDim.y = s_header.uTexHeight;
	_sImage.aMipMaps.Clear();
	_sImage.aMipMaps.PushBack( 0 );
	_sImage.aData.Resize( u_size, false );
	
	if ( !Image::DeconvertFormat( ifmt, fmt, type, _sImage.eFormat ) )
		return false;

	if ( !Image::SetSwizzle( swizzle, _sImage.eSwizzle ) && !Image::AutoSwizzle( _sImage.eFormat, _sImage.eSwizzle ) )
		return false;

	pFile->ReadS( a_chars_rect, sizeof(a_chars_rect) );
	pFile->ReadS( _sImage.aData.ptr(), u_size );

	
	//Flip height
	int		i_line_w = u_size / _sImage.vTexDim.y;
	uint8	*p_line  = new uint8[i_line_w];
	uint8	*p_data  = _sImage.aData.ptr();

	for (uint i = 0; i < _sImage.vTexDim.y/2; ++i)
	{
		memcpy( (void*)&p_line[0], (void*)&p_data[i*i_line_w], i_line_w);
		memcpy( (void*)&p_data[i*i_line_w], (void*)&p_data[(_sImage.vTexDim.y-(i+1))*i_line_w], i_line_w );
		memcpy( (void*)&p_data[(_sImage.vTexDim.y-(i+1))*i_line_w], (void*)&p_line[0], i_line_w );
	}

	delete[] p_line;

	_aChars.Resize( 224, false );
	for (uint i = 0; i < 224; ++i)
	{
		_aChars[i].x = (float)a_chars_rect[i].x / (float)s_header.uTexWidth;
		_aChars[i].y = (float)a_chars_rect[i].y / (float)s_header.uTexHeight;
		_aChars[i].w = _aChars[i].x + (float)a_chars_rect[i].w / (float)s_header.uTexWidth;
		_aChars[i].h = _aChars[i].y + (float)a_chars_rect[i].h / (float)s_header.uTexHeight;
		_aChars[i].y = 1.f - _aChars[i].y;
		_aChars[i].h = 1.f - _aChars[i].h;
	}

	_uASCIIoffset   = 32;
	_uUnicodeOffset = 0;

	return true;
}


bool CFontConverter::_LoadFontFreeType(IFilePtr pFile)
{
	return false;
}
